//
//  Matrix.cpp
//  GSEngine
//
//  Created by Гукун Александр on 1/30/14.
//  Copyright (c) 2014 SNK. All rights reserved.
//

#include "Matrix3.h"

namespace GSEngine
{
    // default constructor
    mat3::mat3()
    {
        m11 = 1; m12 = 0; m13 = 0;
        m21 = 0; m22 = 1; m23 = 0;
        m31 = 0; m32 = 0; m33 = 1;
    }
    // constructor with float array
    mat3::mat3(float* array)
    {
        for (int i = 0; i < 9; i++)
            m[i] = array[i];
    }
    // constructor with floats
    mat3::mat3(float n11, float n12, float n13,
               float n21, float n22, float n23,
               float n31, float n32, float n33)
    {
        m11 = n11; m12 = n12; m13 = n13;
        m21 = n21; m22 = n22; m23 = n23;
        m31 = n31; m32 = n32; m33 = n33;
    }
    // minor
    float mat3::minor(int i, int j)
    {
        float elements[4];
        int count = 0;
        for (int indexI = 0; indexI < 3; indexI++)
            for (int indexJ = 0; indexJ < 3; indexJ++)
                if (indexI != i && indexJ != j)
                {
                    elements[count] = M[indexI][indexJ];
                    count++;
                }
        return (elements[0] * elements[3]  -  elements[1] * elements[2]);
    }
    // determinant
    float mat3::determinant()
    {
        return (m11*m22*m33 - m11*m23*m32 - m12*m21*m33 + m12*m23*m31 + m13*m21*m32 - m13*m22*m31);
    }
    // set matrix identity
    void mat3::setIdentity()
    {
        m11 = 1.0f; m12 = 0.0f; m13 = 0.0f;
        m21 = 0.0f; m22 = 1.0f; m23 = 0.0f;
        m31 = 0.0f; m32 = 0.0f; m33 = 1.0f;
    }
    // transpose matrix
    void mat3::transpose()
    {
        float temp = m12; m12 = m21; m21 = temp;
              temp = m13; m13 = m31; m31 = temp;
              temp = m23; m23 = m32; m32 = temp;
    }
    // inverse matrix
    void mat3::inverse()
    {
        float delta = determinant();
        if (delta != 0)
        {
            float s = 1 / delta;
            mat3::mat3 complements;
            for (int i = 0; i < 3; i++)
                for (int j = 0; j < 3; j++)
                {
                    float elemSign = pow(-1, double( i+1 + j+1 ));
                    complements.M[i][j] =  elemSign *  minor(i, j);
                }
            
            complements.transpose();
            for (int i = 0; i < 9; i++)
                m[i] = complements.m[i] * s;
        }
        else
            std::cout << "\nWARNING !!! Determinant = 0. This matrix can't be inversed.\n";
    }
    // print matrix
    void mat3::print()
    {
        std::cout << std::endl;
        std::cout << m11 << " " << m12 << " " << m13 << std::endl;
        std::cout << m21 << " " << m22 << " " << m23 << std::endl;
        std::cout << m31 << " " << m32 << " " << m33 << std::endl;
        std::cout << std::endl;
    }
    
    // operator = mat3
    void mat3::operator=(mat3 matrix)
    {
        for (int i = 0; i < 9; i++)
            m[i] = matrix.m[i];
    }
    // operator = float array
    void mat3::operator=(float* array)
    {
        for (int i = 0; i < 9; i++)
            m[i] = array[i];
    }
    // operator * float value
    mat3 mat3::operator*(float value)
    {
        mat3 result;
        for (int i = 0; i < 9; i++)
            result.m[i] = m[i] * value;
        return result;
    }
    // operator * mat3
    mat3 mat3::operator*(mat3 matrix)
    {
        return mat3( m11 * matrix.m11 + m12 * matrix.m21 + m13 * matrix.m31,
                     m11 * matrix.m12 + m12 * matrix.m22 + m13 * matrix.m32,
                     m11 * matrix.m13 + m12 * matrix.m23 + m13 * matrix.m33,
                     m21 * matrix.m11 + m22 * matrix.m21 + m23 * matrix.m31,
                     m21 * matrix.m12 + m22 * matrix.m22 + m23 * matrix.m32,
                     m21 * matrix.m13 + m22 * matrix.m23 + m23 * matrix.m33,
                     m31 * matrix.m11 + m32 * matrix.m21 + m33 * matrix.m31,
                     m31 * matrix.m12 + m32 * matrix.m22 + m33 * matrix.m32,
                     m31 * matrix.m13 + m32 * matrix.m23 + m33 * matrix.m33);
    }
    // returns pointer to float
    const float* mat3::getArrayPtr()
    {
        return &m[0];
    }
}